package view

import (
	"cloudops/src/common"
	"cloudops/src/config"
	"cloudops/src/models"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/go-playground/validator/v10"
	"go.uber.org/zap"
	"strconv"
)

// 创建工单流程
func createProcess(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.Process
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析新增流程请求失败",
			zap.Any("流程", reqObj),
			zap.Error(err),
		)
		common.FailWithMessage(err.Error(), c)
		return
	}

	err = validate.Struct(reqObj)
	if err != nil {
		if error, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(
				gin.H{
					"翻译前": err.Error(),
					"翻译后": error.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return
	}

	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("获取用户信息失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("获取用户信息失败：%v", err.Error()), c)
		return
	}
	reqObj.UserID = dbUser.ID
	// 创建流程
	err = reqObj.CreateProcess()
	if err != nil {
		sc.Logger.Error("创建工单流程失败",
			zap.Any("流程", reqObj),
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("创建工单流程失败：%v", err.Error()), c)
		return
	}
	common.OkWithMessage("创建工单流程成功", c)
}

// 查看工单流程
func getProcessList(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	currentPage, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
	pageSize, _ := strconv.Atoi(c.DefaultQuery("limit", "100"))
	offset := 0
	limit := 0
	limit = pageSize
	if currentPage > 1 {
		offset = (currentPage - 1) * limit
	}
	// 从数据库中拿到所有的工单做分页限制
	objs, err := models.GetProcessAllWithLimitOffset(limit, offset)
	if err != nil {
		sc.Logger.Error("查看工单流程失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("查看工单流程失败：%v", err.Error()), c)
		return
	}

	for _, obj := range objs {
		obj := obj
		obj.FillFrontAllData()
	}

	resp := &ResponseResourceCommon{
		Total: models.GetProcessCount(),
		Items: objs,
	}

	common.OkWithDetailed(resp, "查看工单流程", c)
}

// 更新工单流程
func updateProcess(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	var reqObj models.Process
	err := c.ShouldBindJSON(&reqObj)
	if err != nil {
		sc.Logger.Error("解析更新流程请求失败",
			zap.Any("流程", reqObj),
			zap.Error(err),
		)
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 字段校验
	err = validate.Struct(reqObj)
	if err != nil {
		if error, ok := err.(validator.ValidationErrors); ok {
			common.ReqBadFailWithWithDetailed(
				gin.H{
					"翻译前": err.Error(),
					"翻译后": error.Translate(trans),
				},
				"请求出错",
				c,
			)
			return
		}
		common.ReqBadFailWithMessage(err.Error(), c)
		return
	}

	dbObj, err := models.GetProcessById(int(reqObj.ID))
	if err != nil {
		sc.Logger.Error("获取工单流程失败",
			zap.Any("流程", reqObj),
			zap.Error(err),
		)
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 删除更新的判断
	toModNodes := []models.FlowNode{}
	toDelNodes := []models.FlowNode{}
	toAddNodes := []models.FlowNode{}

	dbNodeMap := make(map[uint]models.FlowNode)
	reqNodeMap := make(map[uint]models.FlowNode)
	for _, dbNode := range dbObj.FlowNodes {
		dbNodeMap[dbNode.ID] = dbNode
	}
	for _, reqNode := range reqObj.FlowNodes {
		reqNodeMap[reqNode.ID] = reqNode
	}

	// 在db里面，但不在请求里面
	for _, node := range dbObj.FlowNodes {
		_, ok := reqNodeMap[node.ID]
		if !ok {
			toDelNodes = append(toDelNodes, node)
		}
	}

	// 如果在请求里面，不是数据库里面
	for _, reqNode := range reqObj.FlowNodes {
		dbNode, ok := dbNodeMap[reqNode.ID]
		if !ok {
			toAddNodes = append(toAddNodes, reqNode)
			continue
		}
		if dbNode.Type != reqNode.Type || dbNode.DefineUserOrGroup != reqNode.DefineUserOrGroup {
			toModNodes = append(toModNodes, reqNode)
		}
	}
	sc.Logger.Info("更新流程关联审批节点前统计",
		zap.Any("流程", reqObj.Name),
		zap.Any("新增数量", len(toAddNodes)),
		zap.Any("删除数量", len(toDelNodes)),
		zap.Any("变更数量", len(toModNodes)),
	)
	err = reqObj.UpdateOneProcess()
	if err != nil {
		sc.Logger.Error("更新工单流程失败",
			zap.Any("流程", reqObj),
			zap.Error(err),
		)
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 关联的审批节点，有可能是新增，也有可能变更了，还有可能删除
	// 遍历需要更新的更新
	for _, node := range toModNodes {
		err = node.UpdateOneFlowNode()
		if err != nil {
			sc.Logger.Error("更新流程关联审批节点失败",
				zap.Any("流程", reqObj),
				zap.Any("审批", node),
				zap.Error(err),
			)
			common.FailWithMessage(err.Error(), c)
			return
		}
	}

	// 遍历需要删除的删除
	for _, node := range toDelNodes {
		err = node.DeleteOneFlowNode()
		if err != nil {
			sc.Logger.Error("删除流程关联审批节点失败",
				zap.Any("流程", reqObj),
				zap.Any("审批", node),
				zap.Error(err),
			)
			common.FailWithMessage(err.Error(), c)
			return
		}
	}

	err = reqObj.UpdateFlowNodes(reqObj.FlowNodes)
	if err != nil {
		sc.Logger.Error("更新流程关联审批节点失败",
			zap.Any("流程", reqObj),
			zap.Error(err),
		)
		common.FailWithMessage(err.Error(), c)
		return
	}
	common.OkWithMessage("更新成功", c)
}

// 删除工单流程
func deleteProcess(c *gin.Context) {
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)
	id := c.Param("id")
	sc.Logger.Info("删除工单流程", zap.Any("id", id))

	// 先去数据库中查找工单流程ID是否存在
	intVar, _ := strconv.Atoi(id)
	dbRole, err := models.GetProcessById(intVar)
	if err != nil {
		sc.Logger.Error("获取工单流程失败",
			zap.Any("id", id),
			zap.Error(err),
		)
		common.FailWithMessage(err.Error(), c)
		return
	}

	// 做个保护，查询一下template中有没有用到这个ID
	dbTemplate, err := models.GetWorkOrderTemplateByProcessId(intVar)
	if dbTemplate != nil {
		sc.Logger.Error("工单流程还在使用中，不能删除", zap.Any("表单设计", id))
		common.FailWithMessage("工单流程还在使用中，不能删除", c)
		return
	}
	err = dbRole.DeleteOneProcess()
	if err != nil {
		sc.Logger.Error("删除工单流程失败",
			zap.Any("流程", id),
			zap.Error(err),
		)
		common.FailWithMessage(err.Error(), c)
		return
	}
	common.OkWithMessage("删除成功", c)
}
